import numpy as np
import pandas as pd
import seaborn as sn
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import xgboost as xgb
from sklearn.preprocessing import OneHotEncoder
from sklearn.metrics import mean_squared_error
from sklearn.metrics import explained_variance_score
import matplotlib.pyplot as plt
import lime
import lime.lime_tabular
Wykorzystałem zbiór danych z PD1. Początek jest zatem analogiczny. Wczytanie danych, sprawdzenie czy są braki i sprawdzenie typów danych
data=pd.read_csv("kc_house_data.csv")
data.head()
print('braki: ',data.isnull().values.any())
data.dtypes
Zmienne dla których nie ma sensu liczyć statystyk tj. średniej przetwarzam na zmienne kategorialne.
# zmienne kategorialne - konwersja
data.condition = pd.Categorical(data.condition)
data.bedrooms = pd.Categorical(data.bedrooms)
data.floors = pd.Categorical(data.floors)
#data.waterfront = pd.Categorical(data.waterfront)
data.view = pd.Categorical(data.view)
data.zipcode = pd.Categorical(data.zipcode)
Sprawdzam rozpiętość dat. Jest niewielka ~rok, więc usuwam tę kolumnę.
data.date=pd.to_datetime(data['date'])
max(data.date)-min(data.date)
#rozstęp dat około rok - można usunąć kolumnę
data=data.drop(columns=['id','date'])
Sprawdzenie korelacji zmiennych - najwyższa około 0.8
Przygotowanie danych pod XGboost
condition = pd.get_dummies(data.condition, prefix = 'condition')
bedrooms = pd.get_dummies(data.bedrooms, prefix = 'bedrooms')
floors = pd.get_dummies(data.floors, prefix = 'floors')
view = pd.get_dummies(data.view, prefix = 'view')
#zipcode = pd.get_dummies(data.zipcode, prefix = 'category')
data = pd.concat([data, condition, bedrooms, floors, view], axis=1)
data=data.drop(columns=['condition','bedrooms','floors','view','zipcode'])
Podział przygotowanego zbioru pod XGB
y = data.price
X = data.drop(['price'], axis=1).select_dtypes(exclude=['object'])
train_X, test_X, train_y, test_y = train_test_split(X.values, y.values, test_size=0.25)
Zadanie 1: Zastosowanie modelu
model = xgb.XGBRegressor(objective='reg:squarederror',booster="gbtree",n_estimators=100, learning_rate=0.08, gamma=0, subsample=0.75, colsample_bytree=1, max_depth=7)
model.fit(X_train, y_train)
preds = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, preds))
print("RMSE: %f" % (rmse))
Zadanie 2 i 3
explainer = lime.lime_tabular.LimeTabularExplainer(train_X, feature_names=X.columns, class_names=['price'], verbose=True, mode='regression')
Zadanie 4: wykresy poszczególnych obserwacji. Dla poniższych obserwacji można zauważyć: brak widoku na morze zawsze jest cechą negatywną, a jego obecność - pozytywną. Szerokość geograficzna niejako "klasteryzuje" dane. Wartość <=47.47 wpływa negatywnie, natomiast w przedziale 47.57 < lat <= 47.68 pozytywnie. Intuicyjnie i zgodnie z oczekiwaniami działa również zmienna "grade": wysoka podnosi cenę, niska obniża.
exp = explainer.explain_instance(test_X[2], model.predict, num_features=10)
exp.show_in_notebook(show_table=False)
exp = explainer.explain_instance(test_X[100], model.predict, num_features=10)
exp.show_in_notebook(show_table=False)
np.unique(test_X[:,3])
np.where(test_X[:,3]==1)
exp = explainer.explain_instance(test_X[121], model.predict, num_features=10)
exp.show_in_notebook(show_table=False)
np.unique(test_X[:,9])
np.where(test_X[:,9]<47.47)
exp = explainer.explain_instance(test_X[2], model.predict, num_features=10)
exp.show_in_notebook(show_table=False)
exp = explainer.explain_instance(test_X[4], model.predict, num_features=10)
exp.show_in_notebook(show_table=False)
Zadanie 5: Regresja liniowa
regressor = LinearRegression()
regressor.fit(X_train,y_train)
Powtórzenie wykresu dla modelu XGBoost (dla porównania)
exp = explainer.explain_instance(test_X[9], model.predict, num_features=10)
exp.show_in_notebook(show_table=False)
Wyjaśnienie dla regresji liniowej
exp = explainer.explain_instance(test_X[9], regressor.predict, num_features=10)
exp.show_in_notebook(show_table=False)
Komentarz: Rozkład znaczących cech jest zgoła odmienny dla tej obserwacji w dwóch modelach. Dotyczy to głównie ich wagi (w modelu XGBoost najbardziej negatywną cechą jest "grade", natomiast w modelu liniowym jest ona dopiero czwartym co do wielkości czynnikiem.
exp = explainer.explain_instance(test_X[4], regressor.predict, num_features=10)
exp.show_in_notebook(show_table=False)
Dla powyższej obserwacji (indeks numer 4) można powtórzyć w zasadzie to, co było napisane powyżej. Zmienia się kolejność zmiennych ze względu na ich ważność, ale najbardziej znaczące z nich mają tę samą tendencję - zawsze negatywną bądź zawsze pozytywną.
Dla danych cen domów z King County i techniki Lime wyniki obu modeli są dość zbieżne, to znaczy dana cecha ma zwykle ten sam rodzaj wpływu.